package com.study.demo.aop.demo2.parser;

import com.study.demo.aop.demo2.ElvinOperation;
import com.study.demo.aop.demo2.annotation.Elvinable;
import com.study.demo.aop.demo2.ElvinableOperation;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;

/**
 * @author: elvin
 * @time: 2019-05-23 14:42
 * @desc:
 **/
public class SpringElvinAnnotationParser implements ElvinAnnotationParser, Serializable {

    @Override
    @Nullable
    public Collection<ElvinOperation> parseElvinAnnotations(Class<?> type) {
        SpringElvinAnnotationParser.DefaultElvinConfig defaultConfig = new SpringElvinAnnotationParser.DefaultElvinConfig();
        return parseElvinAnnotations(defaultConfig, type);
    }

    @Override
    @Nullable
    public Collection<ElvinOperation> parseElvinAnnotations(Method method) {
        SpringElvinAnnotationParser.DefaultElvinConfig defaultConfig = new SpringElvinAnnotationParser.DefaultElvinConfig();
        return parseElvinAnnotations(defaultConfig, method);
    }

    @Nullable
    protected Collection<ElvinOperation> parseElvinAnnotations(SpringElvinAnnotationParser.DefaultElvinConfig cachingConfig, AnnotatedElement ae) {
        Collection<ElvinOperation> ops = parseElvinAnnotations(cachingConfig, ae, false);
        if (ops != null && ops.size() > 1 && ae.getAnnotations().length > 0) {
            Collection<ElvinOperation> localOps = parseElvinAnnotations(cachingConfig, ae, true);
            if (localOps != null) {
                return localOps;
            }
        }
        return ops;
    }

    @Nullable
    private Collection<ElvinOperation> parseElvinAnnotations(
            SpringElvinAnnotationParser.DefaultElvinConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {

        Collection<ElvinOperation> ops = null;

        Collection<Elvinable> elvinables = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Elvinable.class) :
                AnnotatedElementUtils.findAllMergedAnnotations(ae, Elvinable.class));
        if (!elvinables.isEmpty()) {
            ops = lazyInit(null);
            for (Elvinable elvinable : elvinables) {
                ops.add(parseElvinableAnnotation(ae, cachingConfig, elvinable));
            }
        }

        return ops;
    }

    private <T extends Annotation> Collection<ElvinOperation> lazyInit(@Nullable Collection<ElvinOperation> ops) {
        return (ops != null ? ops : new ArrayList<>(1));
    }

    ElvinableOperation parseElvinableAnnotation(AnnotatedElement ae, SpringElvinAnnotationParser.DefaultElvinConfig defaultConfig, Elvinable elvinable) {
        ElvinableOperation.Builder builder = new ElvinableOperation.Builder();

        builder.setName(ae.toString());
        defaultConfig.applyDefault(builder);
        ElvinableOperation op = builder.build();

        return op;
    }

    @Override
    public boolean equals(Object other) {
        return (this == other || other instanceof SpringElvinAnnotationParser);
    }

    @Override
    public int hashCode() {
        return SpringElvinAnnotationParser.class.hashCode();
    }


    /**
     * Provides default settings for a given set of cache operations.
     */
    static class DefaultElvinConfig {

        public DefaultElvinConfig() {

        }

        /**
         * Apply the defaults to the specified {@link ElvinOperation.Builder}.
         * @param builder the operation builder to update
         */
        public void applyDefault(ElvinOperation.Builder builder) {

        }
    }

}
